<?php
/**
 * Created by PhpStorm.
 * Script Name: MessageHandler.php
 * Create: 2022/12/26 9:16
 * Description: 处理器基类
 * Author: fudaoji<fdj@kuryun.cn>
 */

namespace plugin\wechat\app\platform\handler\mp;

use app\common\service\Media as MediaService;
use plugin\wechat\app\service\Mp as MpService;
use plugin\wechat\app\service\MpFollow as FollowService;
use plugin\wechat\app\model\MpMsg;
use plugin\wechat\app\model\MpMaterial;
use plugin\wechat\app\model\MpSpecial;
use plugin\wechat\app\model\MpRule;
use plugin\wechat\app\model\MpFollow;
use EasyWeChat\Kernel\Contracts\EventHandlerInterface;
use EasyWeChat\Kernel\Messages\Image;
use EasyWeChat\Kernel\Messages\Message;
use EasyWeChat\Kernel\Messages\Music;
use EasyWeChat\Kernel\Messages\News;
use EasyWeChat\Kernel\Messages\Text;
use EasyWeChat\Kernel\Messages\Video;
use EasyWeChat\Kernel\Messages\Voice;
use app\common\model\App as AppM;

class MessageHandler implements EventHandlerInterface
{
    /**
     * @var MpMsg
     */
    protected $mpMsgM;

    /**
     * @var MpMaterial
     */
    protected $materialM;
    /**
     * @var MpSpecial
     */
    private $specialM;
    /**
     * @var MpRule
     */
    private $ruleM;
    /**
     * @var MpFollow
     */
    private $followM;

    /**
     * @inheritDoc
     */
    public function __construct()
    {
        $this->mpMsgM = new MpMsg();
        $this->specialM = new MpSpecial();
        $this->ruleM = new MpRule();
        $this->followM = new MpFollow();
        $this->materialM = new MpMaterial();
    }

    public function __get($name)
    {
        $request = \request();
        switch ($name){
            case 'followInfo':
                return $request->followInfo;
                break;
            case 'mpInfo':
                return $request->mpInfo;
                break;
            case 'mpApp':
                return $request->mpApp;
                break;
        }
        return null;
    }

    /**
     *
     * @param null $payload
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \Psr\SimpleCache\InvalidArgumentException
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function handle($payload = null)
    {
        FollowService::getFollowInfo($payload['FromUserName']);
        $this->recordFollowMsg($payload);
    }

    /**
     * 针对关键词的回复
     * @param string $keyword
     * @param Message $message
     * @return string
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function replyKeyword($keyword, $message){
        $res = '';
        $mp_info = request()->mpInfo;
        $rule = $this->ruleM->where([
            ['company_id' ,'=', $mp_info['company_id']],
            ['keyword','=', $keyword],
            ['mp_id','=', $mp_info['id']]
        ])->find();

        if(empty($rule)){ //不存在关键词则特殊回复
            switch ($keyword){
                case 'openid':  //系统保留回复
                    $res = new Text($message['FromUserName']);
                    break;
                case 'appid':  //系统保留回复
                    $res = new Text($mp_info['appid']);
                    break;
                default:
                    $res = $this->replySpecial(MpService::DEFAULT_ANS, $message);
                    break;

            }
        } else { //关键词精确回复
            $material_type = $rule['material_type'];
            if($rule['material_type'] != MediaService::APP){
                $media = $this->materialM->where([
                    ['id' ,'=', $rule['material_id']],
                    ['company_id','=', $mp_info['company_id']],
                    ['mp_id' ,'=', $mp_info['id']]
                ])->find();
            }else{
                $media = AppM::find($rule['material_id']);
            }

            if(!empty($media)){
                $method = camel_case('reply' . $material_type);
                try {
                    $res = $this->$method($media, $message);
                }catch (\Exception $e){
                    $err = is_string($e->getMessage()) ? $e->getMessage() : json_encode($e->getMessage(), JSON_UNESCAPED_UNICODE);
                    dao_log()->error($err);
                }
            }
        }
        return $res;
    }

    /**
     * 特殊消息的回复
     * @param string $event
     * @param Message $message
     * @return bool
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * * Author: fudaoji<fdj@kuryun.cn>
     */
    public function replySpecial($event, $message){
        $res = '';
        $mp_info = request()->mpInfo;
        $special = $this->specialM->where([
            ['event' ,'=', $event],
            ['mp_id' ,'=', $mp_info['id']],
            ['company_id','=', $mp_info['company_id']],
            ['status','=', 1],
        ])->find();
        if($special){
            if(!empty($special['keyword'])){
                //关键词回复
                $res = $this->replyKeyword($special['keyword'], $message);
            }else{
                //应用回复
                $media = AppM::where('name', $special['app_name'])->find();
                $res = $this->replyApp($media, $message);
            }
        }

        if($res){
            return $res;
        }
    }

    /**
     * 交给应用处理
     * @param array $app
     * @param \EasyWeChat\Kernel\Messages\Message $message
     * @return string
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function replyApp($app, $message){
        $res = '';
        $app_name = $app['name'];

        $class = '\\plugin\\' . $app_name . '\\app\\platform\\controller\\Mp';
        if (class_exists($class)) {
            $obj = new $class;
            if (method_exists($obj, 'message')) {
                try {
                    $res = $obj->message($message);
                }catch (\Exception $e){
                    dao_log('wechat')->error($e->getMessage());
                }
            }
        }
        return $res;
    }

    /**
     * 文本回复
     * @param array $media
     * @return Text
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function replyText($media = []){
        $text = MessageObj::text($media);
        $this->recordMpReply($text);
        return $text;
    }

    /**
     * 图片回复
     * @param array $media
     * @return Image
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function replyImage($media = []){
        $image = MessageObj::image($media);
        $this->recordMpReply($image);
        return $image;
    }

    /**
     * 语音回复
     * @param array $media
     * @return Voice
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function replyVoice($media = []){
        $voice = MessageObj::voice($media);
        $this->recordMpReply($voice);
        return $voice;
    }

    /**
     * 视频回复
     * @param array $media
     * @return Video
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function replyVideo($media = []){
        $video = MessageObj::video($media);
        $this->recordMpReply($video);
        return $video;
    }

    /**
     * 音乐回复
     * @param array $media
     * @return Music
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function replyMusic($media = []){
        $music = MessageObj::music($media);
        $this->recordMpReply($music);
        return $music;
    }

    /**
     * 图文回复
     * @param array $media
     * @return News
     * Author: fudaoji<fdj@kuryun.cn>
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function replyNews($media = []){
        $news = MessageObj::news($media);
        $this->recordMpReply($news);
        return $news;
    }

    /**
     * 消息公共处理，例如记录粉丝消息
     * @param $message
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function recordFollowMsg($message){
        $request = request();
        $mp_info = $request->mpInfo;
        $follow_info = $request->followInfo;
        try{
            if(! in_array($message['MsgType'], ['event'])){
                $request->mpMsg = $this->mpMsgM->create([
                    'company_id' =>  $mp_info['company_id'],
                    'mp_id' => $mp_info['id'],
                    'type' => $message['MsgType'],
                    'openid' => $message['FromUserName'],
                    'content' => json_encode($message, JSON_UNESCAPED_UNICODE),
                    'nickname' => $follow_info['nickname'],
                    'headimgurl' => $follow_info['headimgurl']
                ]);
            }
        }catch (\Exception $e){
            dao_log()->error(json_encode($e->getMessage(), JSON_UNESCAPED_UNICODE));
        }
    }

    /**
     * 公众号回复粉丝
     * @param $media
     */
    public function recordMpReply($media){
        $mp_info = request()->mpInfo;
        $mp_msg = request()->mpMsg;
        if(!empty($mp_msg)){
            try{
                $type = $media->getType();
                $media_array = $media->toXmlArray();
                $content = $type == 'text' ? $media_array : $media_array[ucfirst($type)];

                $this->mpMsgM->create([
                    'company_id' => $mp_info['company_id'],
                    'pid' => $mp_msg['id'],
                    'openid' => $mp_msg['openid'],
                    'mp_id' => $mp_info['id'],
                    'type' => $type,
                    'is_reply' => 1,
                    'content' => json_encode($content, JSON_UNESCAPED_UNICODE),
                    'nickname' => $mp_info['nick_name'],
                    'headimgurl' => $mp_info['head_img']
                ]);
                $this->mpMsgM->update([
                    'id' => $mp_msg['id'],
                    'company_id' => $mp_info['company_id'],
                    'status' => 1
                ]);
            }catch (\Exception $e){
                dao_log()->error(json_encode($e->getMessage(), JSON_UNESCAPED_UNICODE));
            }
        }
    }

    /**
     * 客服回复
     * @param $media
     * @param $to
     * Author: fudaoji<fdj@kuryun.cn>
     */
    public function replyCustomerService($media, $to){
        $this->mpApp->customer_service->message($media)->to($to)->send();
        $this->recordMpReply($media);
    }
}